/* $Id: db.c 57 2007-03-25 09:14:34Z aldleonm $
 */
#include "db.h"
#include "encdb.h"
#include "extern.h"

extern struct _dbi *db_i;
extern struct _dbid *db_id;

/* @file encdb.h
 * @brief open database
 * 	DB *dbh : empty database descriptor
 * 	int new : create a new database
 *	- note: this function depends on 'extern... db_i'
 * @param DB *dbh , int new 
 */
int
OpenDB(DB * dbh)
{
	int ret;

	/*
	if ((ret = dbh->set_encrypt(dbh, db_i->passw, DB_ENCRYPT_AES)) != 0) {
		dbh->err(dbh, ret, "DB->set_encrypt");
		return (EXIT_FAILURE);
	} else {
		memset(db_i->passw, 0, strlen(db_i->passw));
	}
	*/

	if (db_i->action == CREATE) {
		if ((ret=dbh->open(dbh, NULL, db_i->fname, db_i->name, DB_BTREE,
				 DB_CREATE | DB_EXCL, 0600)) != 0) 
		{
			dbh->err(dbh, ret, "DB->open");
			return (EXIT_FAILURE);
		}
		return (EXIT_SUCCESS);
	} else  {
		if ( db_i->action == WRITE ) {
			ret=dbh->open(dbh, NULL, db_i->fname, db_i->name, 
				DB_BTREE, 0, 0600);
		} else  {
			ret=dbh->open(dbh, NULL, db_i->fname, db_i->name, 
				DB_BTREE, DB_RDONLY, 0600);
		}
		if ( ret != 0 ) {
			dbh->err(dbh, ret, "DB->open");
			return (EXIT_FAILURE);
		}
	}

	return (EXIT_SUCCESS);
}

/* @file encdb.h
 * @brief query and of return the entry and size
 * @param DB *dbh, char *qkey, u_int32_t rsize
 */
void *
QueryDB(DB * dbh, char *qkey, u_int32_t * rsize)
{

	int ret = 0;
	DBT key, data;
	DBC *dbc;

	/* init memory area */
	memset(&key, 0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.size = strlen(qkey);
	key.data = qkey;

	data.ulen = 0;	/* see berkelydb-4.2+ doc: DBT */
	data.flags = DB_DBT_REALLOC;

	/* get cursor */
	if ((ret = dbh->cursor(dbh, NULL, &dbc, 0)) != 0) {
		dbh->err(dbh, ret, "DB->cursor");
		return NULL;
	}

	if (debug == 1)
		printf("Searching....\n");
	/* get entry */
	if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) == 0) {
		if (debug == 1) {
			fprintf(stderr, "key.data: found: %s\n", qkey);
			fprintf(stderr, "key.size size: %d\n", key.size);
			fprintf(stderr, "data.size size: %d\n", data.size);
		}

		if ((ret = dbc->c_close(dbc)) != 0) {
			fprintf(stderr, "QueryDB(): error closing cursor.\n");
			return NULL;
		}

		*rsize = data.size;
		return data.data;
	}

	/* close cursor */
	if (ret == DB_NOTFOUND) {
		fprintf(stderr, "Key %s not forund in database %s.\n", qkey, db_i->name);
		return NULL;
	}
	dbh->err(dbh, ret, "DB->c_get");
	return NULL;
}

/* @file encdb.h
 * @brief: list all keys of a database 
 * @param: DB * dbh
 */
int
ListDB(DB * dbh)
{
	DBT key, data;
	DBC *dbcursor;
	u_int32_t tsize = 0;
	int tentries = 0;
	int ret = 0;
	char *rkeyname;

	memset(&key, 0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	if ((ret = dbh->cursor(dbh, NULL, &dbcursor, 0)) != 0) {
		dbh->err(dbh, ret, "DB->cursor");
		return (EXIT_FAILURE);
	}

	if (debug == 1)
		fprintf(stderr, "starting to list entries.\n");
	while ((ret = dbcursor->c_get(dbcursor, &key, &data, DB_NEXT)) == 0) {
		tsize = tsize + key.size + data.size;
		tentries = tentries + 1;
		rkeyname = malloc(key.size + 1);	/* +1 for the NULL */
		memset(rkeyname, 0, key.size + 1);
		if (debug == 1) {
			fprintf(stderr, "=====\nkey name: %s\n",
				memcpy(rkeyname, key.data, key.size));
			fprintf(stderr, "key/data size: %u\n", key.size + data.size);
		}
		fprintf(stdout, "%s,%u,%u\n",
			memcpy(rkeyname, key.data, key.size), key.size, data.size);
	}

	if (ret != DB_NOTFOUND) {
		dbh->err(dbh, ret, "DB->c_get");
		return (EXIT_FAILURE);
	} else {
		fprintf(stdout, "==========\ntotal keys: %u\n", tentries);
		fprintf(stdout, "total size of all key/data: %u\n==========\n", tsize);

		if ((ret = dbcursor->c_close(dbcursor)) != 0) {
			dbh->err(dbh, ret, "DB->c_get");
			return EXIT_FAILURE;
		}
		return (EXIT_SUCCESS);
	}
}

/* @file encdb.h
* @dbrief write to database
* @param dbhandle, keyname, datapart, datasize
*/
int
WriteDB(DB * dbh, char *keyname, void *datapart, u_int32_t datasize)
{

	int ret;
	DBT key;
	DBT data;

	memset(&key, 0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.size = strlen(keyname);
	key.data = keyname;

	data.size = datasize;
	data.data = datapart;

	if ((ret = dbh->put(dbh, NULL, &key, &data, DB_NOOVERWRITE)) == 0) {
		if (debug == 1)
			printf("Wrote: %d\n", data.size);
		db_i->item_counter += 1;
		/* free(data.data); */
		return (EXIT_SUCCESS);
	} else {
		dbh->err(dbh, ret, "DB->put");
		/* free(data.data); */
		return (EXIT_FAILURE);
	}

	/* NOT REACHED */
}


/* @file encdb.h
* @brief delete a single entry in database
* @param key to delete 
* @return none zero on error 
*/
int
DeleteDB(DB * dbh, char *qkey)
{

	DBT key, data;
	DBC *dbcursor;
	int ret;

	memset(&key, 0, sizeof(DBT));
	memset(&data, 0, sizeof(DBT));

	key.size = strlen(qkey);
	key.data = qkey;

	if ((ret = dbh->del(dbh, NULL, &key, 0)) != 0) {
		dbh->err(dbh, ret, "DB->del");
		return (EXIT_FAILURE);
	}

	return (EXIT_SUCCESS);
}

/* @file encdb.h
 * @brief close DB and DBC pointers
 * @param DB
 * @return zero on sucesss
 */
void
CloseDB(DB * dbh)
{

	int ret;

	if (dbh != NULL) {
		if (debug == 1)
			fprintf(stderr, "Closing/Flushing DB obj...\n");
		if ((ret = dbh->close(dbh, 0)) != 0) {
			dbh->err(dbh, ret, "DB->close");
		}
	}
}

/* @file encdb.h
 * @brief Outputs statistics from database.
 * @param DB
 * @return zero on sucesss
 */
int
StatDB(DB * dbh)
{
	DB_BTREE_STAT *edb_stat;
	void *tmp_p;
	int ret = 0;

#if DB_VERSION_MINOR == 2 || DB_VERSION_MINOR == 3
	if ((ret = dbh->stat(dbh, &tmp_p, 0)) != 0) {
#elif DB_VERSION_MINOR == 4
	if ((ret = dbh->stat(dbh, NULL, &tmp_p, 0)) != 0) {
#endif
		dbh->err(dbh, ret, "DB->stat");
		return (EXIT_FAILURE);
	}

	/* XXX nice :) */
	edb_stat = (DB_BTREE_STAT *) tmp_p;

	fprintf(stdout, "Statistics of %s...\n", db_i->name);
	fprintf(stdout, "bt_magic: %u\n", edb_stat->bt_magic);
	fprintf(stdout, "bt_version: %u\n", edb_stat->bt_version);
	fprintf(stdout, "bt_nkeys: %u\n", edb_stat->bt_nkeys);
	fprintf(stdout, "bt_ndata: %u\n", edb_stat->bt_ndata);
	fprintf(stdout, "bt_pagesize: %u\n", edb_stat->bt_pagesize);
	fprintf(stdout, "bt_minkey: %u\n", edb_stat->bt_minkey);
	fprintf(stdout, "bt_re_len: %u\n", edb_stat->bt_re_len);
	fprintf(stdout, "bt_re_pad: %u\n", edb_stat->bt_re_pad);
	fprintf(stdout, "bt_levels: %u\n", edb_stat->bt_levels);
	fprintf(stdout, "bt_int_pg: %u\n", edb_stat->bt_int_pg);
	fprintf(stdout, "bt_leaf_pg: %u\n", edb_stat->bt_leaf_pg);
	fprintf(stdout, "bt_dup_pg: %u\n", edb_stat->bt_dup_pg);
	fprintf(stdout, "bt_over_pg: %u\n", edb_stat->bt_over_pg);
	fprintf(stdout, "bt_free: %u\n", edb_stat->bt_free);
	fprintf(stdout, "bt_int_pgfree: %u\n", edb_stat->bt_int_pgfree);
	fprintf(stdout, "bt_leaf_pgfree: %u\n", edb_stat->bt_leaf_pgfree);
	fprintf(stdout, "bt_dup_pgfree: %u\n", edb_stat->bt_dup_pgfree);
	fprintf(stdout, "bt_over_pgfree: %u\n\n", edb_stat->bt_over_pgfree);

	return (EXIT_SUCCESS);
}

/* END */
